package com.jse;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.regex.Pattern;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.jse.json.JsonObject;
import com.jse.tpl.AWriter;

public class Lang {
	
	public static final List<String> javakeyWord=List.of("abstract","assert","boolean","break","byte","case","catch",
			"char","class","const","continue","default","do","double","else","enum","extends","final","finally",
			"float","for","goto","if","implements","import","instanceof","int","interface","long","native","new",
			"package","private","protected","public","return","strictfp","short","static","super","switch",
			"synchronized","this","throw","throws","transient","try","void","volatile","while");
	
	private static final String STR62="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";//_-
	private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
	private static final char[] CHAR62 = STR62.toCharArray();
	private static final char[] CHAR64 = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
	
	/** 加密安全SecureRandom 默认推荐L64X128MixRandom<br/> 单线程最快Xoroshiro128PlusPlus 32位或仅一个L32X64StarStarRandom或L32X64MixRandom<br/>
	静态多线程Xoroshiro128PlusPlus 或 Xoshiro256PlusPlus<br/> 静态多线程L64X128MixRandom||L64X256MixRandom,<br/>再大L128X128MixRandom 或 L128X256MixRandom<br/>
	连续生成L64X256MixRandom(4等分) L64X1024MixRandom(10等分)<br/> 超大L64X256MixRandom、L64X1024MixRandom、L128X256MixRandom 或 L128X1024MixRandom */
	final static RandomGenerator RANDOM = RandomGeneratorFactory.of("L128X1024MixRandom").create();//new SplittableRandom();//RandomGeneratorFactory.of("SplittableRandom").create();//单线程最快

	public static int rand(int max) {return rand(0,max);}
	public static int rand(int min,int max) {return RANDOM.nextInt(min,max+1);}
	public static String randomString(int len) {
		return randomString(STR62,len);
	}
	public static String randomString(String s, int len) {
		if (s==null||s.isEmpty()) {
			return "";
		}
		final StringBuilder sb = new StringBuilder(len);
		if (len < 1) {
			len = 1;
		}
		int baseLength = s.length();
		for (int i = 0; i < len; i++) {
			int number = rand(baseLength-1);
			sb.append(s.charAt(number));
		}
		return sb.toString();
	}
	

	public static boolean isEmpty(Object o) {
		if(o==null)return true;
		else if(o instanceof Collection<?> c)return c.isEmpty();
		else if(o instanceof Map<?,?> c)return c.isEmpty();
		else if(o instanceof CharSequence c)return c.isEmpty()||c.equals("null")||c.equals("undefined");
		else if(o.getClass().isArray())return Array.getLength(o)==0;
		else if(o instanceof Iterable<?> c)return c.iterator() != null;
		else if(o instanceof Iterator<?> c)return c.hasNext();
		else if(o.getClass().getName().endsWith("nashorn.internal.runtime.Undefined"))return true;
		return false;
	}
	public static boolean isNotEmpty(Object o) {return !isEmpty(o);}
	public static boolean isMobile(String ua) {return ua!=null&&(isAndroid(ua)||isIOS(ua));}
	public static boolean isIOS(String ua){ua=ua.toLowerCase();return ua.contains("iphone")||ua.contains("ipad")||ua.contains("ipod");}
	public static boolean isAndroid(String ua) {return ua.toLowerCase().contains("android");}
	public static boolean isWeChat(String ua) {return ua.toLowerCase().contains("micromessenger");}

	public static byte[] serialize(Object object) {
		if (object == null) {
			return null;
		}
		ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
		try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
			oos.writeObject(object);
			oos.flush();
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex);
		}
		return baos.toByteArray();
	}

	public static Object deserialize(byte[] bytes) {
		if (bytes == null) {
			return null;
		}
		try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
			return ois.readObject();
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Failed to deserialize object", ex);
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException("Failed to deserialize object type", ex);
		}
	}
	
	public static Properties properties(String path) {
		try{
			InputStream in=null;
			if(Fs.exists(path)) {
				in=new FileInputStream(path);
			}else {
				in=Jse.clazz.getResourceAsStream(path);
			}
			Properties props = new Properties();
			props.load(new InputStreamReader(in));
			return props;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	
	
	public static String md5(String srcStr){
		return hash("MD5", srcStr);
	}
	
	public static String sha1(String srcStr){
		return hash("SHA-1", srcStr);
	}
	
	public static String sha256(String srcStr){
		return hash("SHA-256", srcStr);
	}
	
	public static String sha384(String srcStr){
		return hash("SHA-384", srcStr);
	}
	
	public static String sha512(String srcStr){
		return hash("SHA-512", srcStr);
	}
	public static byte[] hmacSHA256(String secret, String message) throws Exception {
      Mac hmacSha256 = Mac.getInstance("HmacSHA256");
      SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
      hmacSha256.init(secret_key);
      return hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
	}
	
	public static String hash(String algorithm, String srcStr) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			byte[] bytes = md.digest(srcStr.getBytes("utf-8"));
			return toHex(bytes);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String toHex(byte[] bytes) {
		StringBuilder ret = new StringBuilder(bytes.length * 2);
		for (int i=0; i<bytes.length; i++) {
			ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
			ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
		}
		return ret.toString();
	}
	
	/**
	 * md5 128bit 16bytes
	 * sha1 160bit 20bytes
	 * sha256 256bit 32bytes
	 * sha384 384bit 48bytes
	 * sha512 512bit 64bytes
	 */
	public static String salt(int saltLength) {
		StringBuilder salt = new StringBuilder(saltLength);
		ThreadLocalRandom random = ThreadLocalRandom.current();
		for (int i=0; i<saltLength; i++) {
			salt.append(CHAR62[random.nextInt(CHAR62.length)]);
		}
		return salt.toString();
	}
	
	public static String saltSha256() {
		return salt(32);
	}
	
	public static String saltSha512() {
		return salt(64);
	}
	
	// 生成随机字符串
	public static String randString(int strLength) {
		return salt(strLength);
	}
	
	public static final long FNV_OFFSET_BASIS_64 = 0xcbf29ce484222325L;
	public static final long FNV_PRIME_64 = 0x100000001b3L;
	public static long fnv1a64(String key) {
	long hash = FNV_OFFSET_BASIS_64;
		for(int i=0, size=key.length(); i<size; i++) {
			hash ^= key.charAt(i);
			hash *= FNV_PRIME_64;
		}
		return hash;
	}
	
	public static String toFixed(Object o,int n){return String.format("%.2f",Casts.toDouble(o)).toString();}
	
	public static ClassLoader loader() {
		ClassLoader ret = Thread.currentThread().getContextClassLoader();
		return ret != null ? ret : Lang.class.getClassLoader();
	}
	
	public static int random() {
		return ThreadLocalRandom.current().nextInt();
	}
	public static <T> Set ofSet(Object...args){return new HashSet<Object>(Arrays.asList(args));}
	public static <T> Map ofMap(Object...args) {
		JsonObject m=new JsonObject();
		for (int i = 0; i < args.length; i++) {
			m.put(args[i].toString(),args[i+1]);
			i++;
		}
		return m;
	}
	public static String format(Object o,String fmt) {
		if(o instanceof Date d)return Times.format(fmt,d);
		if(fmt.contains("#")) {
			DecimalFormat df = new DecimalFormat(fmt);
			df.setRoundingMode(RoundingMode.HALF_UP);
			return df.format(o);
		}
		throw new RuntimeException("obj不支持格式化:"+fmt);
	}
	
	
	
	public static String uuid() {return UUID.randomUUID().toString().replace("-","");}
	public static String uuid36() {return UUID.randomUUID().toString();}
	public static Date now() {return new Date();}
	
	private static long snowflake_timestamp=System.currentTimeMillis();
    private static long snowflake_sequence = 0;//序列
    
    public static long snowflake() {return snowflake(0);}
    /**
     * 返回下一个key. 注意服务器时间不要调整
     * @return 下一个key
     */
    public static synchronized long snowflake(int node) throws RuntimeException {
        snowflake_sequence++;
        if (snowflake_sequence > 4095) {//当前时间小于1693843249454 2023年小于当前时间 时间不对服务器调整了 
            snowflake_timestamp = System.currentTimeMillis();//3882312855551L 2093年最大时间戳超过70年
            snowflake_sequence = 0;
        }//此算法编码完成的时间 1523289600000L 
        return ((snowflake_timestamp - 1523289600000L) << 22) | (node << 12) | snowflake_sequence;
    }
    public synchronized long nextId() {return nextId(0);}
    public synchronized long nextId(int node) {//1420041600000L
        long sequenceTmp = snowflake_sequence;
        snowflake_sequence = (snowflake_sequence + 1) & 4095;
        if (snowflake_sequence == 0 && sequenceTmp >= 0) {
        	snowflake_timestamp += 1;
        }// 移位并通过或运算拼到一起组成64位的ID
        return ((snowflake_timestamp - 1420041600000L) << 22)| (node << 12)| snowflake_sequence;
    }
    
    public static String millisId(String s) {return System.currentTimeMillis()+s;}
    public static String nanotimeId(String s) {return System.nanoTime()+s;}
    public static String timeId() {return Times.format("yyyyMMddHHmmssSSS");}
    public static String timeId(String s) {return Times.format("yyyyMMddHHmmssSSS").concat(s);}
    public static String nanoId(){return nanoId(RANDOM,CHAR62,21);}
    public static String nanoId(int size){return nanoId(RANDOM,CHAR62,size);}
    public static String nanoId(RandomGenerator random,char[] alphabet,int size) {
       int mask = (2 << (int)Math.floor(Math.log((double)(alphabet.length-1)) / Math.log(2.0D))) - 1;
       int step = (int)Math.ceil(1.6D * (double)mask * (double)size / (double)alphabet.length);
       StringBuilder idBuilder = new StringBuilder();
       while(true) {
        byte[] bytes = new byte[step];
        RANDOM.nextBytes(bytes);
        for(int i=0;i<step;++i) {
          int alphabetIndex = bytes[i] & mask;
          if (alphabetIndex < alphabet.length) {
           idBuilder.append(alphabet[alphabetIndex]);
          if (idBuilder.length() == size) {
           return idBuilder.toString();
          }
         }
        }
       }
    }

	public static <T> T mapToBean(Map map,Class<T> beanClass) {
		try {
			if(Map.class.isAssignableFrom(beanClass))return (T)map;
			T obj = beanClass.newInstance();
			BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (!map.containsKey(key)) {
					continue;
				}
				Object value = map.get(key);
				Method setter = property.getWriteMethod();// 得到property对应的setter方法
				setter.invoke(obj, value);
			}
			return obj;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	public static String escape(String str, AWriter w) throws IOException {
		StringBuilder sb=new StringBuilder();
		for (int i = 0, len = str.length(); i < len; i++) {
			char cur = str.charAt(i);
			switch (cur) {
			case '<':
				sb.append("&lt;");
				break;
			case '>':
				sb.append("&gt;");
				break;
			case '"':
				sb.append("&quot;");
				break;
			case '\'':
				sb.append("&#39;");// IE 不支持 &apos; 考虑 &#39;
				break;
			case '&':
				sb.append("&amp;");
				break;
			default:
				sb.append(cur);
				break;
			}
		}
		return sb.toString();
	}
	public static Object def(Object o,Object d) {
		return o==null?d:o;
	}
	public static Object bean(String name) {return Ioc.get(name);}
	public static boolean startsWith(String s,Collection<String> c) {
		return c.parallelStream().anyMatch(s::startsWith);
	}
	public static String startsWithGet(String s,Collection<String> c) {
		return c.parallelStream().filter(s::startsWith).findAny().orElse(null);
	}
	public static boolean isPresent(String name) {
		try {loader().loadClass(name);return true;
        } catch (ClassNotFoundException e) {return false;}
	}
	public static JsonObject beanToMap(Object bean) {
		return new JsonObject(bean);
	}
	public static JsonObject javabeanToMap(Object obj) {
		if (obj == null) {
			return null;
		}
		JsonObject map = new JsonObject();
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (key.equals("class")) {// 过滤class属性
					continue;
				}
				Method getter = property.getReadMethod();// 得到property对应的getter方法
				Object value = getter.invoke(obj);
				map.put(key, value);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return map;
	}
	
private static final Map<String,Pattern> patterns=new HashMap<>();
	
	public static Pattern pattern(String regex) {
		if(patterns.containsKey(regex)) return patterns.get(regex);
		else {var pattern=Pattern.compile(regex);patterns.put(regex,pattern);return pattern;}
	}
	
	public static boolean match(String regex, String value) {
		return pattern(regex).matcher(value).find();
	}
	
	public static int parseInt(Object o) {
		if(o==null)return -1;
		if(o instanceof Number n)return n.intValue();
		return Double.valueOf("1.2").intValue();
	}
	
	public static float parseFloat(Object o) {
		if(o==null)return -1.0f;
		if(o instanceof Number n)return n.floatValue();
		return Float.parseFloat(o.toString());
	}
	public static double parseDouble(Object o) {
		if(o==null)return -1.0f;
		if(o instanceof Number n)return n.doubleValue();
		return Double.parseDouble(o.toString());
	}
	
	public static String className() {
		return new Throwable().getStackTrace()[1].getClassName(); //获取调用者的类名
	}
	public static String methodName() {
		return new Throwable().getStackTrace()[1].getMethodName(); //获取调用者的方法名 
	}
	public static String classMethodName() {
		var t=new Throwable().getStackTrace()[1];
		//t.getFileName()+"="+t.getLineNumber()+
		return t.getClassName()+"."+t.getMethodName(); //获取调用者的方法名 
	}
	
	public static void main(String[] args) {
		long s1=System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
//			new BigDecimal("2.1").intValue();
//			Double.valueOf("1.2").intValue();
		}
		long s2=System.currentTimeMillis();
		System.out.println(s2-s1);
		System.out.println(classMethodName());
	}
}
